package debtbundler.domain.entity.impl;

import java.util.logging.Level;
import java.util.logging.Logger;

import debtbundler.domain.entity.BalanceBook;
import debtbundler.domain.entity.BalanceSheet;
import debtbundler.domain.entity.MoneyPool;
import debtbundler.domain.entity.Transaction;


/**
 * A facade class that "aggregates" entity update events, making it possible 
 * to listen to only one object to be informed of all changes. Thread-safe.
 * 
 * @author Anssi
 *
 */
public class EntityUpdateNotifier extends AbstractListenableEntity implements EntityListener, ListenableEntity {
	private static final long serialVersionUID = 1L;
	private static Logger logger = Logger.getLogger(EntityUpdateNotifier.class.getName());
	
	protected static EntityUpdateNotifier theInstance = null;

	
	/**
	 * Returns a singleton <code>EntityUpdateNotifier</code> instance that
	 * passes on update events of all entities created by <code>EntityManager</code>.
	 * 
	 * @return	a singleton <code>EntityUpdateNotifier</code> instance	
	 */
	public static EntityUpdateNotifier getTheInstance() {
		if (theInstance == null) {
			theInstance = new EntityUpdateNotifier();
		}
		return theInstance;
	}


	@Override
	public synchronized void modelStateChanged(ListenableEntity source) {
		logger.log(Level.FINER, "Passing on update event in entity {0} of {1}", new Object[]{source, source.getClass()});
		
		if (listeners != null) {
			for (EntityListener listener : listeners) {
				listener.modelStateChanged(source);
			}
		}
	}
	
	/**
	 * Sets this notifier to listen to the given balance book
	 * and all of its current balance sheets by calling
	 * <code>listenTo(BalanceSheet)</code>.
	 * 
	 * @param book	the balance book to listen to
	 * @see listenTo(BalanceSheet)
	 */
	public void listenTo(BalanceBook book) {
		book.addEntityListener(this);
		for (BalanceSheet sheet : book.getBalanceSheets()) {
			listenTo(sheet);
		}
	}
	
	/**
	 * Sets this notifier to listen to the given balance sheet
	 * and all of its current money pools by calling
	 * <code>listenTo(MoneyPool)</code>.
	 * 
	 * @param sheet	the balance sheet to listen to
	 * @see listenTo(MoneyPool)
	 */
	public void listenTo(BalanceSheet sheet) {
		sheet.addEntityListener(this);
		for (MoneyPool pool : sheet.getMoneyPools()) {
			listenTo(pool);
		}
	}
	
	/**
	 * Sets this notifier to listen to the given money pool and
	 * all of its current transactions by calling 
	 * <code>listenTo(Transaction)</code>.
	 * 
	 * @param pool	the money pool to listen to
	 * @see listenTo(Transaction)
	 */
	public void listenTo(MoneyPool pool) {
		( (AbstractMoneyPoolImpl) pool).addEntityListener(this);
		for (Transaction tr : pool.getTransactions()) {
			listenTo(tr);
		}
	}

	/**
	 * Sets this notifier to listen to the given transaction.
	 *  
	 * @param tr	the transaction to listen to
	 */
	public void listenTo(Transaction tr) {
		( (AbstractTransactionImpl) tr).addEntityListener(this);
	}
}
